package com.kgcx.pay.util;

import com.kgcx.common.eum.CommonDataEeum;
import com.kgcx.common.utils.MD5Util;
import com.kgcx.common.utils.XMLUtil;
import com.kgcx.common.utils.http.HttpUtils;
import com.xiaoleilu.hutool.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * 第三方 微信支付
 * @author qinze
 *
 */
@Service
@Slf4j
public class WeChatUtil {
	
	/**
	 * 乘客端
	 */
	private final static String PASSENGER_APP_ID = "wx7ca7da377be892c9";
	
	private final static String PASSENGER_MCH_ID = "1494243492";
	
	/**
	 * 司机端
	 */
	private final static String DRIVER_APP_ID = "wxd8d186e4eb9aba0c";
	
	private final static String DRIVER_MCH_ID = "1494240982";
	
	/**
	 * 小程序
	 */
	private final static String SMALL_ROUTINE_APP_ID = "wxd24dd16522019ae6";
	
	private final static String SMALL_ROUTINE_MCH_ID = "1488565322";
	
	/**
	 * API秘钥一致
	 */
	private final static String APP_KEY  = "A7E19D1D5334704808F4F099E2BFE4F5";
	
	/**
	 * 微信统一下单接口
	 */
	private final static String PAY_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	
	private final static Integer HTTP_CLIENT_TIME_OUT = 5000;
	
	private final static String BODY  = "交易支付";

	/**
	 * 预支付
	 * @param outTradeNo 商户订单号
	 * @param totalFee 订单总金额，单位为分
	 * @param spbillCreateIp 终端IP
	 * @param notifyUrl 回调地址
	 * @param type 100乘客端 200司机 300小程序端
	 * @param openId
	 * @param payOrderNo 支付订单号
	 * @return
	 */
	public PayPreInfoBean getWeChatPayInfo(String outTradeNo,String totalFee,String spbillCreateIp,String notifyUrl,int type,String openId,String payOrderNo) {
		PayPreInfoBean payPreInfoBean = new PayPreInfoBean();
		String prepayRequest = "";
		if (CommonDataEeum.PAY_TYEP_PASSENGER.getCode() == type) {
			/**
			 * 乘客端
			 */
			 prepayRequest = genPayRequest(BODY,outTradeNo,totalFee,spbillCreateIp,notifyUrl,PASSENGER_APP_ID,PASSENGER_MCH_ID,false,openId,payOrderNo);
		} else if (CommonDataEeum.PAY_TYEP_DRIVER.getCode() == type) {
			/**
			 * 司机端
			 */
			 prepayRequest = genPayRequest(BODY,outTradeNo,totalFee,spbillCreateIp,notifyUrl,DRIVER_APP_ID,DRIVER_MCH_ID,false,openId,payOrderNo);
		} else if (CommonDataEeum.PAY_TYEP_SMALL_ROUTINE.getCode() == type) {
			/**
			 * 小程序端
			 */
			if (StringUtils.isBlank(openId)) {
				payPreInfoBean.setOrderNum(payOrderNo);
				payPreInfoBean.setPayInfo("openId为空");
				payPreInfoBean.setPayStatus(false);
				return payPreInfoBean;
			}
			 prepayRequest = genPayRequest(BODY,outTradeNo,totalFee,spbillCreateIp,notifyUrl,SMALL_ROUTINE_APP_ID,SMALL_ROUTINE_MCH_ID,true,openId,payOrderNo);
		}
		byte[] buf= HttpUtils.httpPost(PAY_URL, prepayRequest, HTTP_CLIENT_TIME_OUT);
		if (buf == null || buf.length < 1) {
			payPreInfoBean.setOrderNum(payOrderNo);
			payPreInfoBean.setPayStatus(false);
			return payPreInfoBean;
		}
		String contentXml = new String(buf);
		Map<String, String> resultMap = XMLUtil.doXMLParse(contentXml);
		log.info("resultMap = {} ",resultMap);
		if (null != resultMap) {
			if (null != resultMap.get("return_code") && "SUCCESS".equals(resultMap.get("return_code"))) {
				if (null != resultMap.get("result_code") && "SUCCESS".equals(resultMap.get("result_code"))) {
					payPreInfoBean.setData(resultMap.get("prepay_id"));
					payPreInfoBean.setOrderNum(payOrderNo);
					payPreInfoBean.setPayInfo("成功.");
					payPreInfoBean.setPayStatus(true);
					// 按顺序
					Long timeStamp = System.currentTimeMillis();
					int wechat_timeStamp = getSecondTimestampTwo(new Date());
					String noceStr = RandomUtil.randomUUID().replaceAll("-", "");
					if (CommonDataEeum.PAY_TYEP_SMALL_ROUTINE.getCode() == type) {
						/**
						 * 小程序
						 */
						Map<String, String> paramsMap = new TreeMap<>();
						paramsMap.put("appId", SMALL_ROUTINE_APP_ID);//应用ID
						paramsMap.put("timeStamp", String.valueOf(timeStamp));
						paramsMap.put("nonceStr", noceStr);//随机字符串
						paramsMap.put("package","prepay_id=" +  payPreInfoBean.getData());
						paramsMap.put("signType", "MD5");
						
						String sign = getSign(paramsMap, APP_KEY);
						payPreInfoBean.setData(sign);
						payPreInfoBean.setPrepayId(resultMap.get("prepay_id"));
						payPreInfoBean.setTimeStamp(String.valueOf(timeStamp));
						payPreInfoBean.setNonceStr(noceStr);
					} else if (CommonDataEeum.PAY_TYEP_PASSENGER.getCode() == type) {
						/**
						 * 乘客端
						 */
						Map<String, String> paramsMap = new TreeMap<>();
						paramsMap.put("appid", PASSENGER_APP_ID);//应用ID
						paramsMap.put("partnerid", PASSENGER_MCH_ID);//商户号
						paramsMap.put("prepayid", resultMap.get("prepay_id"));//预支付交易会话ID
						paramsMap.put("package", "Sign=WXPay");//预支付交易会话ID
						paramsMap.put("noncestr", noceStr);//预支付交易会话ID
						paramsMap.put("timestamp", String.valueOf(wechat_timeStamp));
						
						String sign = getSign(paramsMap, APP_KEY);
						payPreInfoBean.setData(sign);
						payPreInfoBean.setPrepayId(resultMap.get("prepay_id"));
						payPreInfoBean.setTimeStamp(String.valueOf(wechat_timeStamp));
						payPreInfoBean.setNonceStr(noceStr);
						payPreInfoBean.setPackage_str("Sign=WXPay");
						payPreInfoBean.setPartnerId(PASSENGER_MCH_ID);
					} else if (CommonDataEeum.PAY_TYEP_DRIVER.getCode() == type) {
						/**
						 * 司机端
						 */
						Map<String, String> paramsMap = new TreeMap<>();
						paramsMap.put("appid", DRIVER_APP_ID);//应用ID
						paramsMap.put("partnerid", DRIVER_MCH_ID);//商户号
						paramsMap.put("prepayid", resultMap.get("prepay_id"));//预支付交易会话ID
						paramsMap.put("package", "Sign=WXPay");//预支付交易会话ID
						paramsMap.put("noncestr", noceStr);//预支付交易会话ID
						paramsMap.put("timestamp", String.valueOf(wechat_timeStamp));
						
						String sign = getSign(paramsMap, APP_KEY);
						payPreInfoBean.setData(sign);
						payPreInfoBean.setPrepayId(resultMap.get("prepay_id"));
						payPreInfoBean.setTimeStamp(String.valueOf(wechat_timeStamp));
						payPreInfoBean.setNonceStr(noceStr);
						payPreInfoBean.setPackage_str("Sign=WXPay");
						payPreInfoBean.setPartnerId(DRIVER_MCH_ID);
					}
					return payPreInfoBean;
				}
			}
		}
		payPreInfoBean.setPayInfo(resultMap.toString());
		payPreInfoBean.setOrderNum(payOrderNo);
		payPreInfoBean.setPayStatus(false);
		return payPreInfoBean;
	}
	
	
	public PayCallbackBean parserPayCallback(Map<String, String> payRS) {
		String callbackInfo = "";
		PayCallbackBean payCallbackBean = new PayCallbackBean();
		//此字段是通信标识，非交易标识，交易是否成功需要查看result_code来判断
		log.info(String.format("return_code = %s。", payRS.get("return_code")));
		if (!"SUCCESS".equals(payRS.get("return_code"))) {
			callbackInfo = "通信失败。";
			payCallbackBean.setPayStatus(false);
			payCallbackBean.setPayCallBackInfo(callbackInfo);
			return payCallbackBean;
		}
		//校验结果 验签
		boolean signResult = checkParam(payRS, APP_KEY);
		if (!signResult) {
			payCallbackBean.setPayStatus(false);
			payCallbackBean.setPayCallBackInfo("微信验签失败。");
			return payCallbackBean;
		}
		log.info(String.format("result_code = %s。", payRS.get("result_code")));
		if (!"SUCCESS".equals(payRS.get("result_code"))) {
			callbackInfo = payRS.get("err_code_des");
			payCallbackBean.setPayStatus(false);
			payCallbackBean.setPayCallBackInfo(callbackInfo);
			return payCallbackBean;
		}
		String outTradeNo = payRS.get("out_trade_no");// 订单编号
		Long payAmount = Long.parseLong(payRS.get("total_fee"));// 支付金额（单位分）
		log.info(String.format("total_fee = %s。", payAmount));
		payCallbackBean.setPayStatus(true);
		payCallbackBean.setPayCallBackInfo("成功。");
		String transactionId = payRS.get("transaction_id");// 平台订单号
		payCallbackBean.setTransactionId(transactionId);
		payCallbackBean.setOrderSn(outTradeNo);
		payCallbackBean.setPayAmount(String.valueOf(payAmount));
		return payCallbackBean;
	}

	/**
	 * 拼接参数验证
	 * @param body 商品描述
	 * @param out_trade_no 商户订单号
	 * @param total_fee 订单总金额，单位为分
	 * @param spbill_create_ip 终端IP
	 * @param notify_url 回调地址
	 * @param APP_ID 应用ID
	 * @param MCH_ID 商户号
	 * @param isSmallRoutine 是否小程序
	 * @param openId
	 * @param payOrderNo
	 * @return
	 */
	private String genPayRequest(String body,String out_trade_no,String total_fee,
			String spbill_create_ip,String notify_url,String APP_ID,String MCH_ID,Boolean isSmallRoutine,String openId,String payOrderNo) {
		// 按顺序
		String noceStr = RandomUtil.randomUUID().replaceAll("-", "");
		Map<String, String> paramsMap = new TreeMap<>();
		paramsMap.put("appid", APP_ID);//应用ID
		paramsMap.put("mch_id", MCH_ID);//商户号
		paramsMap.put("nonce_str", noceStr);//随机字符串
		paramsMap.put("body", body);//商品描述
		paramsMap.put("out_trade_no",payOrderNo);	//商户订单号
		paramsMap.put("total_fee", total_fee);//订单总金额，单位为分
		paramsMap.put("spbill_create_ip", spbill_create_ip);//终端IP
		paramsMap.put("notify_url",notify_url);	//接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
		paramsMap.put("trade_type", "APP");	//支付类型 APP--app支付
		paramsMap.put("time_start", dateOperation(null));//交易起始时间
		paramsMap.put("time_expire", dateOperation(5));	//订单失效时间 为5分钟
		if (isSmallRoutine) {
			//公众号支付才有的参数
			paramsMap.put("trade_type", "JSAPI");	//支付类型 JSAPI--公众号支付
			paramsMap.put("openid", openId);//用户标识
		}
		String sign = getSign(paramsMap, APP_KEY);
		paramsMap.put("sign", sign);	
		String xmlstring = XMLUtil.toXml(paramsMap);
		return xmlstring;
	}

	public static String getSign(Map<String, String> params, String apiKey) {
		StringBuilder sb = new StringBuilder();
		sb.append(createLinkString(params));
		sb.append("&key=");
		sb.append(apiKey);
		String appSign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
		return appSign;
	}
	
	/**
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {
		// sort
		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);
		String prestr = "";
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			} else {
				prestr = prestr + key + "=" + value + "&";
			}
		}
		return prestr;
	}
	
	 public static boolean checkParam(Map<String,String> params,String key){
	        boolean result = false;
	        if(params.containsKey("sign")){
	            String sign = params.get("sign");
	            params.remove("sign");
	            StringBuilder buf = new StringBuilder((params.size() +1) * 10);
	            buildPayParams(buf,params,false);
	            String preStr = buf.toString();
	            String signRecieve = sign(preStr, "&key=" + key, "utf-8");
	            result = sign.equalsIgnoreCase(signRecieve);
	        }
	        return result;
	    }
	 
	public static String sign(String text, String key, String input_charset) {
		text = text + key;
		return DigestUtils.md5Hex(getContentBytes(text, input_charset));
	}

	private static byte[] getContentBytes(String content, String charset) {
		if (charset == null || "".equals(charset)) {
			return content.getBytes();
		}
		try {
			return content.getBytes(charset);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
		}
	}

	  public static void buildPayParams(StringBuilder sb,Map<String, String> payParams,boolean encoding){
	        List<String> keys = new ArrayList<String>(payParams.keySet());
	        Collections.sort(keys);
	        for(String key : keys){
	            sb.append(key).append("=");
	            if(encoding){
	                sb.append(urlEncode(payParams.get(key)));
	            }else{
	                sb.append(payParams.get(key));
	            }
	            sb.append("&");
	        }
	        sb.setLength(sb.length() - 1);
	    }
	  
	public static String urlEncode(String str) {
		try {
			return URLEncoder.encode(str, "UTF-8");
		} catch (Throwable e) {
			return str;
		}
	}
	
	/**
	 * 当前时间 +minute(分钟) 为null则返回当前时间 
	 * 格式:yyyyMMddHHmmss
	 * @param minute
	 * @return
	 */
	private static String dateOperation(Integer minute) {
		SimpleDateFormat dft = new SimpleDateFormat("yyyyMMddHHmmss");
		Calendar nowTime = Calendar.getInstance();
		if (null != minute) {
			nowTime.add(Calendar.MINUTE, minute);
		}
		return dft.format(nowTime.getTime());
	}
	
	public static int getSecondTimestampTwo(Date date){  
	    if (null == date) {  
	        return 0;  
	    }  
	    String timestamp = String.valueOf(date.getTime()/1000);  
	    return Integer.valueOf(timestamp);  
	} 

	public static void main(String[] args) {
//		WeChatUtil weChatUtil = new WeChatUtil();
//		System.out.println(weChatUtil.getWeChatPayInfo("3444", "1", "127.0.0.1", "www.baidu.com", 100,"o0y4C0QEF67ybH4XyN_hUL9KlT9g","payO").getData());
		System.out.println(dateOperation(null));
	}
}
